#ifndef __M_CONNECTION_H__
#define __M_CONNECTION_H__

// #include "/home/zhong/muduo/muduo/net/TcpClient.h"
#include "../third/include/muduo/net/TcpClient.h"

#include "../third/include/muduo/net/EventLoopThread.h"
#include "../third/include/muduo/net/TcpConnection.h"
#include "../third/include/muduo/base/CountDownLatch.h"

#include <iostream>
#include <functional>
#include <string>
#include <memory>

#include "channel.hpp"
#include "worker.hpp"

namespace zhong
{
    class Connection
    {
    public:
        using ptr = std::shared_ptr<Connection>;
        Connection(const std::string &server_ip, int server_port, const AsyncWorker::ptr &worker)
            : _latch(1),
              _client(worker->_loop_thread.startLoop(), muduo::net::InetAddress(server_ip, server_port), "Client"),
              _dispatcher(std::bind(&Connection::onUnknownMessage, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3)),
              _codec(std::make_shared<ProtobufCodec>(std::bind(&ProtobufDispatcher::onProtobufMessage, &_dispatcher, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3))),
              _worker(worker),
              _channel_manager(std::make_shared<ChannelManager>())
        {

            // 添加回调函数给函数分发器，当获取到某个类型的数据，自动调用对应的函数
            _dispatcher.registerMessageCallback<zhong::basicResponse>(std::bind(&Connection::onBasicResponse,
                                                                                this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
            _dispatcher.registerMessageCallback<zhong::basicConsumeResponse>(std::bind(&Connection::onBasicConsumeResponese,
                                                                                       this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
            _dispatcher.registerMessageCallback<zhong::basicUserResponse>(std::bind(&Connection::onBasicUserResponse,
                                                                                    this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
            // 初始化获取连接函数和获取消息函数

            _client.setMessageCallback(std::bind(&ProtobufCodec::onMessage, _codec.get(), std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
            _client.setConnectionCallback(std::bind(&Connection::onConnection, this, std::placeholders::_1));

            _client.connect();
            _latch.wait(); // 进行阻塞等待，直到连接建立成功
        }

        Channel::ptr openChannel()
        {
            Channel::ptr channel = _channel_manager->create(_connection, _codec);
            // 注意我们在客户端进行创建信道之后，服务端也需要创建这个信道
            if (channel->openChannel() == false) // 该函数内部出现问题
            {
                DEBUG_LOG("创建信道失败");
                return Channel::ptr();
            }
            DEBUG_LOG("创建信道成功[%p]", channel.get());
            return channel;
        }

        void closeChannel(const Channel::ptr &channel)
        {
            DEBUG_LOG("关闭信道成功[%p]", channel.get());
            channel->closeChannel();
            _channel_manager->remove(channel->channel_id());
        }

    private:
        void onBasicResponse(const muduo::net::TcpConnectionPtr &conn, const basicResponsePtr &response, muduo::Timestamp)
        {
            // 获取信道
            Channel::ptr channel = _channel_manager->get(response->channel_id());
            if (channel.get() == nullptr)
            {
                DEBUG_LOG("未找到信道信息");
                return;
            }
            // 将响应对象添加到，信道的基础响应map中
            channel->putBasicResponse(response);
        }
        void onBasicUserResponse(const muduo::net::TcpConnectionPtr &conn, const basicUserResponsePtr &response, muduo::Timestamp)
        {
            // 获取信道
            Channel::ptr channel = _channel_manager->get(response->channel_id());
            if (channel.get() == nullptr)
            {
                DEBUG_LOG("未找到信道信息");
                return;
            }
            // 将响应对象添加到，信道的基础响应map中
            channel->putBasicUserResponse(response);
        }
        void onBasicConsumeResponese(const muduo::net::TcpConnectionPtr &conn, const basicConsumeResponsePtr &response, muduo::Timestamp)
        {
            // 获取信道
            Channel::ptr channel = _channel_manager->get(response->channel_id());
            if (channel.get() == nullptr)
            {
                DEBUG_LOG("未找到信道信息");
                return;
            }
            // 将消息处理任务添加到线程池中

            _worker->_thread_pool.push([&channel, response]()
                                       { channel->consume(response); });
        }
        void onUnknownMessage(const muduo::net::TcpConnectionPtr &conn, const MessagePtr &message, muduo::Timestamp)
        {
            conn->shutdown();
        }
        void onConnection(const muduo::net::TcpConnectionPtr &conn) // 链接建立时发生的回调函数
        {
            if (conn->connected() == true)
            {
                std::cout << "connection success" << std::endl;
                // 连接建立成功时需要唤醒当前的阻塞态
                _connection = conn; // 设置类内的Connection
                _latch.countDown();
            }
            else
            {
                std::cout << "connection closed" << std::endl;
                _connection.reset();
            }
        }

    private:
        // bug 131，类内变量具有先声明后析构！！！

        muduo::CountDownLatch _latch;             // 实现了线程同步，主要是用于等待连接完成后再进行其他操作
        muduo::net::TcpConnectionPtr _connection; // 客户端对应的链接   !!!他要放到client之前，因为client关闭之前要发送close
        muduo::net::TcpClient _client;            // 客户端

        ProtobufDispatcher _dispatcher; // 请求分发器
        ProtobufCodecPtr _codec;        // 协议处理器

        AsyncWorker::ptr _worker; // 工作线程池
        ChannelManager::ptr _channel_manager;
    };
}

#endif